package com.sprite.utils.crypto;

import java.security.KeyFactory;
import java.security.KeyPair;
import java.security.KeyPairGenerator;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.X509EncodedKeySpec;

import javax.crypto.Cipher;

import com.sprite.utils.UtilString;

/**
 * <p>非对称加密，分组加密</p>
 *
 * <h3>秘钥长度：秘钥长度指的不是公私钥的长度，而是指模长（modulus）</h3>
 * <h3>Padding 说明</h3>
 * <p>RSA_PKCS1_PADDING :
 * 输入： 必须 比 RSA 钥模长(modulus) 短至少11个字节, 也就是　RSA_size(rsa) – 11
 * 如果输入的明文过长，必须切割，　然后填充
 * 输出：和modulus一样长</p>
 * <p>RSA_PKCS1_OAEP_PADDING:
 * 输入：RSA_size(rsa) – 41
 * 输出：和modulus一样长
 * </p>
 * <p>RSA_NO_PADDING:
 * 输入：可以和RSA钥模长一样长，如果输入的明文过长，必须切割，然后填充
 * 输出：和modulus一样长
 * </p>
 *
 * @author Jack
 */
public final class UtilRSA {

    public static final String PADDING_NO_PADDING = "NoPadding";
    public static final String PADDING_PKCS1_PADDING = "PKCS1Padding";
    public static final String PADDING_OAEP_PADDING = "OAEPPadding";

    private static volatile KeyFactory keyFactory;

    private static void init() throws Exception {
        if (keyFactory == null) {
            synchronized (UtilRSA.class) {
                if (keyFactory == null) {
                    keyFactory = KeyFactory.getInstance("RSA");
                }
            }
        }
    }

    /**
     * 生成秘钥对
     *
     * @param keySize
     * @return
     * @throws Exception
     */
    public static KeyPair generateKeyPair(int keySize) throws Exception {
        KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance("RSA");
        keyPairGenerator.initialize(keySize);
        return keyPairGenerator.genKeyPair();
    }

    /**
     * 私钥加密
     *
     * @param content
     *         待加密
     * @param key
     *         秘钥
     * @throws Exception
     *         解密错误
     * @return 解密之后的数据
     */
    public static byte[] decryptByPrivateKey(byte[] content, byte[] key, String padding) throws Exception {
        init();
        Cipher cipher = Cipher.getInstance("RSA/ECB/" + padding);
        cipher.init(Cipher.DECRYPT_MODE, keyFactory.generatePrivate(new PKCS8EncodedKeySpec(key)));
        byte[] result = cipher.doFinal(content);
        return result;
    }

    /**
     * 公钥加密
     *
     * @param content
     *         待加密
     * @param key
     *         秘钥
     * @throws Exception
     *         解密错误
     * @return 解密之后的数据
     */
    public static byte[] decryptByPublicKey(byte[] content, byte[] key, String padding) throws Exception {
        init();
        Cipher cipher = Cipher.getInstance("RSA/ECB/" + padding);
        cipher.init(Cipher.DECRYPT_MODE, keyFactory.generatePublic(new X509EncodedKeySpec(key)));
        byte[] result = cipher.doFinal(content);
        return result;
    }

    /**
     * 私钥解密
     *
     * @param content
     *         待加密
     * @param key
     *         秘钥
     * @throws Exception
     *         加密错误
     * @return 加密之后的数据
     */
    public static byte[] encryptByPrivateKey(byte[] content, byte[] key, String padding) throws Exception {
        init();
        Cipher cipher = Cipher.getInstance("RSA/ECB/" + padding);
        cipher.init(Cipher.ENCRYPT_MODE, keyFactory.generatePrivate(new PKCS8EncodedKeySpec(key)));
        byte[] result = cipher.doFinal(content);
        return result;
    }

    /**
     * 公钥解密
     *
     * @param content
     *         待加密
     * @param key
     *         秘钥
     * @throws Exception
     *         加密错误
     * @return 加密之后的数据
     */
    public static byte[] encryptByPublicKey(byte[] content, byte[] key, String padding) throws Exception {
        init();
        Cipher cipher = Cipher.getInstance("RSA/ECB/" + padding);
        cipher.init(Cipher.ENCRYPT_MODE, keyFactory.generatePublic(new X509EncodedKeySpec(key)));
        byte[] result = cipher.doFinal(content);
        return result;
    }

    public static void main(String[] args) throws Exception {
        //Security.addProvider(new org.bouncycastle.jce.provider.BouncyCastleProvider());
        KeyPair keyPair = generateKeyPair(2048);
        String content = "1";
        String string = UtilString.byteToHex((UtilRSA.encryptByPublicKey(content.getBytes(), keyPair.getPublic().getEncoded(), UtilRSA.PADDING_OAEP_PADDING)));
        byte[] bts = UtilRSA.decryptByPrivateKey(UtilString.hexStringToBytes(string), keyPair.getPrivate().getEncoded(), UtilRSA.PADDING_OAEP_PADDING);
        System.out.println(string);

        System.out.println(new String(bts));
    }

}
